# Copyright 2018 the Autoware Foundation
# Co-developed by Tier IV, Inc. and Apex.AI, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



# Add integration tests
#
# :param TESTNAME: a name for the integration test (< 100 characters)
#                  For short command lists, this is optional
#                  The autogenerate name for a long lists of commands
#                  may be too long for some OSes and filesystem, so
#                  you must specify a shorter name.
#                  The corresponding test will be called <TESTNAME>_integration_test.
#                  Otherwise the test name will be <cmd1_cmd2>_integration_test.
# :type TESTNAME: string
# :param COMMANDS: a list of 3-colon-separated (:::) commands in the order they should be launched.
# :type COMMANDS: string
# :param EXPECTED_OUTPUT_DIR: path to a directory containing expected outputs.
#                             The expected outputs are a .regex or .txt files
#                             with the same name as the executables.
# :type EXPECTED_OUTPUT_DIR: string
# :param LABELS: one or more integration test labels.
# :type LABELS: string|list
#
# For example, if you want to first launch 'cmd1' with argument '-a -b' and
# then launch 'cmd2' with arguments '-c -d'. Then you would call:
#
#   integration_tests(
#     EXPECTED_OUTPUT_DIR "test/integration/expected_outputs"
#     COMMANDS "cmd1 -a -b:::cmd2 -c -d")
#
#   EXPECTED_OUTPUT_DIR should contain cmd1.txt/.regex and cmd2.txt/.regex
#
# @public
function(integration_tests)
  cmake_parse_arguments(ARGS "" "TESTNAME;EXPECTED_OUTPUT_DIR;COMMANDS" "LABELS"
      ${ARGN})

  if(ARG_UNPARSED_ARGUMENTS)
    message(FATAL_ERROR "integration_tests() called with unused arguments: "
      "${ARG_UNPARSED_ARGUMENTS}")
  endif()

  if(NOT ARGS_EXPECTED_OUTPUT_DIR)
    message(FATAL_ERROR "You must specify EXPECTED_OUTPUT_DIR. "
      "See instructions in integration_tests/cmake/integration_tests.cmake")
  endif()

  if(NOT ARGS_COMMANDS)
    message(FATAL_ERROR "No commands specified. "
      "See instructions in integration_tests/cmake/integration_tests.cmake")
  endif()

  string(REPLACE ":::" ";" commands "${ARGS_COMMANDS}")

  # Split command into executable and arguments
  set(executables "")
  set(EXEC_ARGS "")
  foreach(command ${commands})
    string(REPLACE " " ";" cmd_plus_args ${command})
    list(GET cmd_plus_args 0 cmd)
    list(APPEND executables ${cmd})
    list(LENGTH cmd_plus_args length_cmd_plus_args)
    if(length_cmd_plus_args GREATER 1)
      list(REMOVE_AT cmd_plus_args 0)
      string(REPLACE ";" " " args "${cmd_plus_args}")
      list(APPEND EXEC_ARGS ${args})  # Add arguments as a space-separated list
    else()
      list(APPEND EXEC_ARGS " ")  # Add empty argument if no arguments
    endif()
  endforeach()

  # The expected output is a file with the same name as the executable
  # with extension .regex or .txt
  set(EXPECTED_OUTPUTS "")
  foreach(executable ${executables})
    string(REPLACE "::" "__" output_file ${executable})
    list(APPEND EXPECTED_OUTPUTS
    "${ARGS_EXPECTED_OUTPUT_DIR}/${output_file}")
  endforeach()

  set(EXECUTABLES "")
  foreach(executable ${executables})
    list(APPEND EXECUTABLES "$<TARGET_FILE:${executable}>")
  endforeach()


  # Fill in template file using EXECUTABLES, EXEC_ARGS, and EXPECTED_OUTPUTS
  string(REPLACE ";" "_" exe_list_underscore_ "${executables}")
  string(REPLACE ":" "_" exe_list_underscore "${exe_list_underscore_}")

  # Check target name length:
  #   Max for encrypted FS: 143 - https://tinyurl.com/yarr4hm8
  #   Max *path* in Windows: 260 - https://tinyurl.com/ybtva3vw
  # Max length = 100 leaves some room for file extensions and paths to file.
  set(MAX_LENGTH "100")
  if(NOT ARGS_TESTNAME)
    set(target_name "${exe_list_underscore}_integration_test")
  else()
    set(target_name "${ARGS_TESTNAME}_integration_test")
  endif()
  string(LENGTH "${target_name}" target_length)
  if("${target_length}" GREATER "${MAX_LENGTH}")
    message(FATAL_ERROR "The target name for the integration test "
      "(${ARG_TESTNAME}) is longer than ${MAX_LENGTH} characters, "
      "which can cause problems in some filesystems and OSes. "
      "If the target name is autogenerated, use the TESTNAME argument "
      "to force a shorter name."
    )
  endif()

  if((TEST "${target_name}") OR (TARGET "${target_name}"))
    message(FATAL_ERROR "Target ${target_name} already exists. Use the TESTNAME "
      "argument to prevent name collisions."
    )
  endif()

  configure_file(
    "${integration_tests_DIR}/test_executables.py.in"
    ${target_name}.py.configured
    @ONLY
  )
  file(GENERATE
    OUTPUT "${CMAKE_BINARY_DIR}/${target_name}_$<CONFIG>.py"
    INPUT "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.py.configured"
  )

  # Add the test
  ament_add_pytest_test(${target_name}
    "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_$<CONFIG>.py"
    TIMEOUT 60
    ENV
    RCL_ASSERT_RMW_ID_MATCHES=rmw_fastrtps_cpp
    RMW_IMPLEMENTATION=rmw_fastrtps_cpp)

  list(APPEND ARGS_LABELS "integration")

  set_tests_properties(
    "${target_name}"
    PROPERTIES
    LABELS "${ARGS_LABELS}"
  )

  foreach(executable ${executables})
      set_property(
        TEST ${target_name}
        APPEND PROPERTY DEPENDS ${executable})
  endforeach()
endfunction()
